home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / sbin / lm-profiler < prev    next >
Text File  |  2009-10-06  |  9KB  |  303 lines

  1. #! /bin/sh
  2.  
  3. # This script assists you in achieving very high power savings on laptops.
  4. # It can detect programs that perform regular, non-bursty disk operations,
  5. # and network services that listen on external addresses. When started,
  6. # lm-profiler will run for 10 minutes (or a configured number of minutes),
  7. # after which it will provide a series of recommendations. 
  8. #
  9. # It will try to find init scripts for any programs that it recommends for 
  10. # stopping, and it will ask if you want to place links to those scripts in 
  11. # /etc/laptop-mode/batt-stop, so that laptop mode tools will automatically 
  12. # stop those daemons when battery mode is detected.
  13. #
  14. #
  15. # This script is a part of Laptop Mode Tools.
  16. #
  17. # Configuration options for this script can be found in
  18. # /etc/laptop-mode/lm-profiler.conf.
  19. #
  20. # Maintainer: Bart Samwel (bart@samwel.tk)
  21. # Adapted from initial version written by Jan Polacek (jerome@ucw.cz).
  22.  
  23.  
  24. #
  25. # Read configuration.
  26. #
  27.  
  28. # Defaults
  29.  
  30. PROFILE_RUN_LENGTH=600
  31. ACTIVITY_INTERVAL_MAX=150
  32. ACTIVITY_INTERVAL_MIN=5
  33. RECOMMEND_DEFAULT_SERVICES=1
  34. DEFAULT_SERVICES="anacron cron atd"
  35. DEF_IGNORE_PROGRAMS="pdflush journald XFree86 acpid apmd lm-profiler dmesg syslogd awk sed grep mc bc xfs cat diff uniq vi mv sort sleep"
  36. IGNORE_PROGRAMS="$DEF_IGNORE_PROGRAMS"
  37. RECOMMEND_NETWORK_SERVICES=1
  38. DEF_IGNORE_NETWORK_SERVICES="perl" # Some daemons run on perl, not very informative
  39. IGNORE_NETWORK_SERVICES="$DEF_IGNORE_NETWORK_SERVICES"
  40. VERBOSE_OUTPUT=0
  41. if [ -f /etc/laptop-mode/lm-profiler.conf ] ; then
  42.     . /etc/laptop-mode/lm-profiler.conf
  43. fi
  44.  
  45. #
  46. # Internal variables
  47. #
  48.  
  49. DEBUG=0
  50. ######################################################################
  51.  
  52. if [ $DEBUG -eq 1 ]; then
  53.     set -eux
  54. fi
  55.  
  56. if [ "$VERBOSE_OUTPUT" -eq 1 ] ; then
  57.     OUTPUT="/dev/stdout"
  58. else
  59.     OUTPUT="/dev/null"
  60. fi
  61.  
  62. if [ ! `id -u` -eq 0 ]; then
  63.   echo "Only root can run profiler."
  64.   exit 0
  65. fi
  66.  
  67. WORKDIR=`mktemp -d -t lm-profiler.XXXXXX`
  68.  
  69.  
  70. start_profiling(){
  71.     # Turn on disk access profilling
  72.     if [ -f /proc/sys/vm/block_dump ]; then
  73.         echo "1" > /proc/sys/vm/block_dump
  74.     else
  75.         echo "/proc/sys/vm/block_dump does not exist, exiting."
  76.         exit 1
  77.     fi
  78. }
  79.  
  80. stop_profiling(){
  81.     # Turn off disk access profilling
  82.     echo "0" > /proc/sys/vm/block_dump
  83. }
  84.  
  85. # Create a commandline for grep, checking for the presence of all
  86. # strings in a space-separated list passed as the first parameter.
  87. format_params(){
  88.     for PARAM in $1 ; do
  89.         echo -n "-e $PARAM " 
  90.     done
  91. }
  92.  
  93. # Detect all processes that have accessed the disk since the last
  94. # invocation of dmesg. The results are written to files called
  95. # "write_accesses_N" and "read_accesses_N", where N is the first
  96. # parameter of this function.
  97. process_dmesg_diff(){
  98.     LEFT="$WORKDIR/dmesg_prev"
  99.     RIGHT="$WORKDIR/dmesg_next"
  100.     dmesg > $RIGHT
  101.     if [ -s $LEFT ] && [ -s $RIGHT ]; then
  102.         # The following command is long and complicated. It does
  103.         # the following things, separately for READ and WRITE
  104.         # accesses:
  105.         # 1. Retrieve only new lines, using diff.
  106.         # 2. Drop the first line -- it is probably a truncated
  107.         #    version of an earlier line.
  108.         # 3. Parse out the name of the process.
  109.         # 4. Filter out IGNORE_PROGRAMS.
  110.         # 5. Write process name to output.
  111.         
  112.         diff -u $LEFT $RIGHT \
  113.             |grep '^+' \
  114.             |grep -o '[^[:space:]]*([0-9]*): WRITE' \
  115.             |sed '1d' \
  116.             |awk -v FS="(" '{print $1}' \
  117.             |grep -v `format_params "$IGNORE_PROGRAMS"` \
  118.             |sort \
  119.             |uniq \
  120.                > $WORKDIR/write_accesses_$1
  121.  
  122.         diff -u $LEFT $RIGHT \
  123.             |grep '^+' \
  124.             |grep -o '[^[:space:]]*([0-9]*): READ' \
  125.             |sed '1d' \
  126.             |awk -v FS="(" '{print $1}' \
  127.             |grep -v `format_params "$IGNORE_PROGRAMS"` \
  128.             |sort \
  129.             |uniq \
  130.                > $WORKDIR/read_accesses_$1
  131.  
  132.         mv $RIGHT $LEFT
  133.         WRITE_ACCESSES_FOUND=0
  134.         for ACCESS in $(cat $WORKDIR/write_accesses_$1) ; do
  135.             if [ $WRITE_ACCESSES_FOUND -eq 0 ] ; then
  136.                 printf '\r                                                                            \rWrite accesses at %d/%d in lm-profiler run:' "$1" "$PROFILE_RUN_LENGTH"
  137.                 WRITE_ACCESSES_FOUND=1
  138.             fi
  139.             echo -n " $ACCESS"
  140.         done
  141.         if [ $WRITE_ACCESSES_FOUND -ne 0 ] ; then
  142.             echo ""
  143.         fi
  144.  
  145.         READ_ACCESSES_FOUND=0
  146.         for ACCESS in $(cat $WORKDIR/read_accesses_$1) ; do
  147.             if [ $READ_ACCESSES_FOUND -eq 0 ] ; then
  148.                 printf '\r                                                                            \rRead accesses at %d/%d in lm-profiler run:' "$1" "$PROFILE_RUN_LENGTH"
  149.                 READ_ACCESSES_FOUND=1
  150.             fi
  151.             echo -n " $ACCESS"
  152.         done
  153.         if [ $READ_ACCESSES_FOUND -ne 0 ] ; then
  154.             echo ""
  155.         fi
  156.     else
  157.         echo "No dmesg data found to profile, exiting."
  158.         exit 1
  159.     fi
  160. }
  161.  
  162. # Attempt to find an init script for ithe process given as an argument
  163. findinit(){
  164.     INITDIR=
  165.     if [ -d /etc/init.d ] ; then
  166.         INITDIR=/etc/init.d
  167.     elif [ -d /etc/rc.d/init.d ] ; then
  168.         INITDIR=/etc/rc.d/init.d
  169.     fi
  170.     if [ "$INITDIR" != "" ] ; then
  171.         INIT=`ls $INITDIR/ |grep ^$1$ |head -n 1`
  172.         if [ -z "$INIT" ]; then
  173.             INIT=`grep $1 $INITDIR/* |sed s/:.*// |head -n 1`
  174.         else
  175.             INIT="$INITDIR/$INIT"
  176.         fi
  177.         if [ ! -z "$INIT" ] && [ -x $INIT ]; then
  178.             echo "$INIT"
  179.         fi
  180.     fi
  181. }
  182.  
  183. # Look for names of running network services
  184. profilenet(){
  185.     netstat -anp |grep ^tcp.*LISTEN |grep -v "Program name" |awk -v FS="/" '{print $2}' |sort |uniq |\
  186.     tr -d ['(',')','[',']']
  187. }
  188.             
  189.  
  190. #
  191. # PROFILING RUN
  192. #
  193.  
  194. # Disable profiling if the script gets interrupted.
  195. trap "stop_profiling; echo; exit 10" EXIT HUP INT ABRT QUIT SEGV TERM
  196.  
  197. SECONDS_DONE=
  198. echo "Profiling run started."
  199. dmesg > $WORKDIR/dmesg_prev
  200. start_profiling
  201. echo > $WORKDIR/write_accesses_$SECONDS_DONE
  202. echo > $WORKDIR/read_accesses_$SECONDS_DONE
  203. SECONDS_DONE=0
  204. while [ $SECONDS_DONE -le $PROFILE_RUN_LENGTH ] ; do
  205.     printf '\r%d seconds elapsed, %d remaining.         \b\b\b\b\b\b\b\b\b' "$SECONDS_DONE" "$(($PROFILE_RUN_LENGTH - $SECONDS_DONE))"
  206.     sleep 1
  207.     SECONDS_DONE=$(($SECONDS_DONE + 1))
  208.     process_dmesg_diff $SECONDS_DONE
  209. done
  210. printf '\r                                                    \r'
  211. stop_profiling
  212. NETPROFILE=`profilenet`
  213. echo "Profiling run completed."
  214.  
  215. #
  216. # OUTPUT
  217. #
  218. ALREADY_SEEN=
  219. if [ "$RECOMMEND_DEFAULT_SERVICES" -ne 0 ] ; then
  220.     for SERVICE in $DEFAULT_SERVICES ; do
  221.         echo
  222.         echo "Program:     \"$SERVICE\""
  223.         echo "Reason:      standard recommendation (program may not be running)"
  224.         INIT=`findinit $SERVICE`
  225.         if [ "$INIT" = "" ] ; then
  226.             echo "Init script: none"
  227.             echo "If you want to disable this program, you should do so manually."
  228.         else
  229.             echo "Init script: $INIT (GUESSED)"
  230.             echo
  231.             echo -n "Do you want to disable this service in battery mode? [y/N]: "
  232.             read ANSWER
  233.             if ( echo "$ANSWER" | grep -i ^y > /dev/null ) ; then
  234.                 ln -fs $INIT /etc/laptop-mode/batt-stop/`echo $INIT | sed 's/.*\///g'`
  235.             fi
  236.         fi
  237.         ALREADY_SEEN="$ALREADY_SEEN $SERVICE"
  238.     done
  239. fi
  240.  
  241.  
  242.  
  243.  
  244. if [ "$RECOMMEND_NETWORK_SERVICES" -ne 0 ] ; then
  245.     for SERVICE in $NETPROFILE ; do
  246.         if ( echo " $IGNORE_NETWORK_SERVICES " | grep -v " $SERVICE " > /dev/null ) ; then
  247.             echo
  248.             echo "Program:     \"$SERVICE\""
  249.             echo "Reason:      listens on network, may not be needed offline."
  250.             INIT=`findinit $SERVICE`
  251.             if [ "$INIT" = "" ] ; then
  252.                 echo "Init script: none"
  253.                 echo "If you want to disable this program, you should do so manually."
  254.             else
  255.                 echo "Init script: $INIT (GUESSED)"
  256.                 echo
  257.                 echo -n "Do you want to disable this service in battery mode? [y/N]: "
  258.                 read ANSWER
  259.                 if ( echo "$ANSWER" | grep -i ^y > /dev/null ) ; then
  260.                     ln -fs $INIT /etc/laptop-mode/batt-stop/`echo $INIT | sed 's/.*\///g'`
  261.                 fi
  262.             fi
  263.             ALREADY_SEEN="$ALREADY_SEEN $SERVICE"
  264.         fi
  265.     done
  266. fi
  267.  
  268. SECONDS_LEFT=$PROFILE_RUN_LENGTH
  269. while [ $SECONDS_LEFT -gt 0 ] ; do
  270.     for SERVICE in `cat $WORKDIR/*_accesses_$SECONDS_LEFT` ; do
  271.         if ( echo " $ALREADY_SEEN " | grep -v " $SERVICE " > /dev/null ) ; then
  272.             CUR_COMPARE_SECONDS=$(($SECONDS_LEFT - $ACTIVITY_INTERVAL_MIN))
  273.             while [ $CUR_COMPARE_SECONDS -gt $(($SECONDS_LEFT - $ACTIVITY_INTERVAL_MAX)) -a $CUR_COMPARE_SECONDS -gt 0 ] ; do
  274.                 if ( grep "^$SERVICE$" $WORKDIR/*_accesses_$CUR_COMPARE_SECONDS > /dev/null ) ; then
  275.                     if ( echo " $ALREADY_SEEN " | grep -v " $SERVICE " > /dev/null ) ; then
  276.                         echo
  277.                         echo "Program:     \"$SERVICE\""
  278.                         echo "Reason:      disk access."
  279.                         INIT=`findinit $SERVICE`
  280.                         if [ "$INIT" = "" ] ; then
  281.                             echo "Init script: none"
  282.                             echo "If you want to disable this program, you should do so manually."
  283.                         else
  284.                             echo "Init script: $INIT (GUESSED)"
  285.                             echo
  286.                             echo -n "Do you want to disable this service in battery mode? [y/N]: "
  287.                         fi
  288.                         read ANSWER
  289.                         if ( echo "$ANSWER" | grep -i ^y > /dev/null ) ; then
  290.                             if [ -e $INIT ] ; then
  291.                                 ln -fs $INIT /etc/laptop-mode/batt-stop/`echo $INIT | sed 's/.*\///g'`
  292.                             fi
  293.                         fi
  294.                         ALREADY_SEEN="$ALREADY_SEEN $SERVICE"
  295.                     fi
  296.                 fi
  297.                 CUR_COMPARE_SECONDS=$(($CUR_COMPARE_SECONDS - 1))
  298.             done
  299.         fi
  300.     done
  301.     SECONDS_LEFT=$(($SECONDS_LEFT - 1))
  302. done
  303.